import { unref, reactive } from 'vue';
import { defHttp } from '/@/utils/http/axios';
import { FlexProps, FormState, InitModalParams, ComponentTypeEnum } from '../types/index';
import { FormSchema, FormProps, FormActionType } from '/@/components/Form/index';
import { ComponentType } from '/@/components/Form/src/types';
import { useRouter } from 'vue-router';
import { useMessage } from '/@/hooks/web/useMessage';
import { useUserStore } from '/@/store/modules/user';
import { mergeFunctions, getCSSValue } from '/@/utils';
import { isObject, isObjectEmpty, isEmpty, isArray } from '/@/utils/is';
import { formatToDate } from '/@/utils/dateUtil';
import { cloneDeep } from 'lodash-es';
import { omit, pick } from 'lodash-es';
import setCustomProps, { setCustomEvent } from './formComponentProps';
import getRules from './formSchemaRules';
// import { verify } from '/@/utils/validator';
import dayjs from 'dayjs';
import {
  FORM_GET_DATA_TYPE,
  getModelInstanceInfoApi,
  commonGetByIdApi,
  commonAddApi,
  commonUpdateApi,
} from '/@/api/config-center/model-design';

import {
  DISABLED_TYPE,
  REQUIRED_TYPE,
  FIELD_TYPE_HIDE,
  TARGET_FIELD_TYPE,
  PASS_FIELD_TYPE,
  DATA_SOURCE_TYPE_TYPE,
  SELECT_COM_TYPE,
  MULTIPLE_SELECT,
  DEFAULT_COM,
  VALUE_FIELD_TYPE,
  SELECT_DEPT_USER_ROLE_POST_TYPE,
} from '../consts';

interface FormInitData {
  btnData: InitModalParams;
  formDesignInfo?: any;
  retFormDesignInfo?: any;
  apiFn?: PromiseFn;
}

const messageFn = useMessage();
const { createMessage } = messageFn;
export class UseModalInit {
  formState: FormState;
  retFormDesignInfo = {};
  callbacks: FormActionType;
  parentFormActionType?: Partial<FormActionType> = {};
  updateInfo: Indexable;
  formDesignInfo: Indexable = {};
  formCustomFn: Indexable<Fn> = {};
  retData = {
    width: '',
    title: '',
    padding: '',
    btns: [],
  };
  constructor(
    { btnData, formDesignInfo = {}, retFormDesignInfo, apiFn }: FormInitData,
    callbacks: FormActionType,
  ) {
    const {
      params = {},
      isUpdate,
      moduleId,
      dataModelId,
      readonly = false,
      partReadonly = false,
      preview = false,
      parentFormActionType,
    } = cloneDeep(btnData);
    const userStore = useUserStore();
    if (!dataModelId) {
      createMessage.error('请绑定表单模型');
      throw '请绑定表单模型';
    }

    //  设置表单外部传递的参数数据，表单整体状态数据
    this.formState = reactive<FormState>({
      moduleId,
      modelId: dataModelId,
      readonly,
      partReadonly,
      preview,
      isUpdate,
      hideFields: [],
      showFields: [],
      hideParams: {},
      params,
      apiFn,
      rangeTimeMap: new Map(),
      userInfo: userStore.getUserInfo,
    });

    this.callbacks = callbacks;
    this.updateInfo = {};
    this.formDesignInfo = formDesignInfo;
    this.retFormDesignInfo = retFormDesignInfo || {};

    this.parentFormActionType = parentFormActionType;
  }

  async initialize() {
    const info = this.formDesignInfo;

    if (!isObject(info) || isObjectEmpty(info)) {
      const { formDesignInfo } = await getModelInstanceInfoApi(
        this.formState.modelId!,
        this.formState.params,
      );

      this.formDesignInfo = formDesignInfo!;
    }

    this.formCustomFn = getFormCustomEvent(this.formDesignInfo);

    const initContext: Indexable = {
      dayjs,
      parentFormActionType: this.parentFormActionType,
      formModel: this.retFormDesignInfo, // 详情数据
      formDesignInfo: this.formDesignInfo,
      userInfo: this.formState.userInfo,
    };
    // {
    //   dayjs,
    //   formActionType: this.callbacks,
    //   parentFormActionType: this.parentFormActionType,
    //   formModel: this.retFormDesignInfo, // 详情数据
    //   formDesignInfo: this.formDesignInfo,
    //   userInfo: this.formState.userInfo,
    // }
    const setFormFieldsValue = this.callbacks.setFieldsValue;

    const initContextParams = [this.formState.params];
    this.callbacks.setFieldsValue = async <T>(values: T) => {
      this.retFormDesignInfo = Object.assign(this.retFormDesignInfo, values);
    };
    this.formCustomFn.beforeInit &&
      (await this.formCustomFn.beforeInit(initContext, initContextParams, defHttp));

    const data = this.retFormDesignInfo;

    if (this.formState.isUpdate && isEmpty(data)) {
      try {
        const { readonly, apiFn, moduleId, params, partReadonly, modelId } = this.formState;
        const ajaxFn = apiFn || commonGetByIdApi;
        let readType = FORM_GET_DATA_TYPE.DEFAULT;
        if (readonly) {
          readType = FORM_GET_DATA_TYPE.READONLY;
        }
        if (partReadonly) {
          readType = FORM_GET_DATA_TYPE.PART_READONLY;
        }

        const ret = await ajaxFn({ moduleId, modelId }, params, readType);
        this.updateInfo = ret;

        this.retFormDesignInfo = Object.assign(this.retFormDesignInfo, ret);
      } catch (e) {
        throw e;
      }
    }

    const params = {
      ...this.formState.params,
      ...this.retFormDesignInfo,
    };
    this.formState.params = params;
    this.formState.echoData = this.retFormDesignInfo;

    const {
      formConfig = {},
      fields,
      hideField,
    } = getFormSchemas(this.formDesignInfo, this.formState, {
      formActionType: this.callbacks,
      parentFormActionType: this.parentFormActionType,
    });

    this.formState.showFields = fields || [];
    this.formState.hideFields = hideField || [];

    await this.callbacks?.setProps?.(formConfig);

    initContext.formActionType = this.callbacks;
    initContext.formModel = this.retFormDesignInfo; // 详情数据
    initContext.formDesignInfo = this.formDesignInfo;
    // <T>(values: T) => Promise<void>
    //  重置 set 方法
    this.callbacks.setFieldsValue = async <T>(values: T) => {
      this.retFormDesignInfo = Object.assign(
        this.retFormDesignInfo,
        cloneDeep(this.retFormDesignInfo),
        values,
      );
      await setFormFieldsValue(values);
    };
    this.formCustomFn.init && this.formCustomFn.init(initContext, initContextParams, defHttp);

    const { btns = [], width = '', title = '', padding = '' } = this.formDesignInfo;

    const retData = {
      title,
      btns,
      padding,
      width,
      value: this.retFormDesignInfo,
    };

    return retData;
  }
  async useModalSumbit(onlyData = false) {
    const initData = this.formState;

    const values = await formValidate(initData, this.callbacks as any);

    const { isUpdate, moduleId, modelId, showFields, partReadonly, readonly } = initData;
    const params = cloneDeep(Object.assign(this.retFormDesignInfo, this.updateInfo, values));

    const afterSumbitContext = {
      formActionType: this.callbacks,
      parentFormActionType: this.parentFormActionType,
      formModel: params,
      messageFn,
      userInfo: this.formState.userInfo,
    };

    const afterSumbitParams = [this.formState.params];

    this.formCustomFn.beforeSumbit &&
      (await this.formCustomFn.beforeSumbit(afterSumbitContext, afterSumbitParams, defHttp));

    console.log(
      '%cSumbit -> params,initParamsData===>209： ',
      'background: rgb(23, 197, 237,.6); color: #ff5023; font-size:18px;font-weight:700',
      params,
      initData,
    );

    // 编辑时存在只读类型则剔除只读字段
    if (unref(isUpdate) && this.formDesignInfo.isReadonly && (readonly || partReadonly)) {
      const data = showFields!.filter(({ updateType }) => updateType === DISABLED_TYPE);
      data.forEach((el) => {
        delete params[el.field];
      });
    }

    afterSumbitContext.formModel = params;

    const fn = this.formCustomFn?.afterSumbit;
    const afterSumbitFn = async function () {
      if (fn) {
        fn(afterSumbitContext, afterSumbitParams, defHttp);
      }
    };
    // 只获取参数不提交后端api
    if (onlyData) {
      return [params, afterSumbitFn];
    }

    //  参数不为空时提交后端api
    if (!isObjectEmpty(params)) {
      const apiFn = unref(isUpdate) ? commonUpdateApi : commonAddApi;
      const data = await apiFn({ moduleId: unref(moduleId), modelId: unref(modelId) }, params);

      if (this.formCustomFn.afterSumbit) {
        await this.formCustomFn?.afterSumbit(
          afterSumbitContext,
          [this.formState.params, data],
          defHttp,
        );

        return [params, data];
      }

      return [params, data];
    }
  }
}

export function getItemColConfig(
  labelSpan: number,
  colSpan: number,
  { layout = 'horizontal', md = 24, flushItemProps, itemColProps, subLabelWidth }: FlexProps,
) {
  //  兼容老配置
  let span = labelSpan;
  if (layout === 'vertical') {
    span = colSpan;
  }

  let itemProps: Indexable = span ? { labelCol: { span } } : {};
  if (flushItemProps) {
    itemProps = flushItemProps;
  }

  // 设置标签宽度
  if (subLabelWidth) {
    itemProps.labelCol = { flex: getCSSValue(subLabelWidth as string) };
  }

  //  兼容老配置
  let colProps = colSpan ? { md, lg: colSpan } : {};

  if (itemColProps) {
    colProps = itemColProps;
  }
  return { itemProps, colProps };
}

function formatData(obj: Recordable, rangeTimeMap) {
  // 格式化时间
  if (rangeTimeMap.size) {
    for (const [key, val] of Object.entries(obj)) {
      // 特殊字段处理

      const valueFormat = rangeTimeMap.get(key);
      if (valueFormat) {
        if (isArray(val)) {
          if (dayjs.isDayjs(val[0])) {
            const f = formatToDate(val[0], valueFormat);
            const e = formatToDate(val[1], valueFormat);
            obj[key] = [f, e];
          }
        } else {
          if (dayjs.isDayjs(val)) {
            obj[key] = formatToDate(val, valueFormat);
          }
        }
      }
    }
  }
}
const formValidate = async (formState: FormState, { validate }) => {
  const values = await validate!();

  formatData(values, formState.rangeTimeMap);
  const { hideFields, showFields, hideParams } = formState;
  // 隐藏字段需提交
  const fields = unref(hideFields);

  const params = { ...values, ...unref(hideParams) };

  if (fields.length) {
    for (const item of fields) {
      const { formModelHiddenType, defaultValue, componentProps = {}, field, targetFiled } = item;

      // 窗口传递参数
      if (formModelHiddenType === PASS_FIELD_TYPE) {
        const router = useRouter() || {};
        const { currentRoute = {} } = router;
        const { query = {} } = unref(currentRoute) as any;
        params[field] = query[field];
      } else if (formModelHiddenType === TARGET_FIELD_TYPE) {
        // 获取录入字段的值对应的label
        const info = showFields.find((el) => el.filed === targetFiled);
        //  查找目标字段属性options
        if (info && info.componentProps.options) {
          //  查找目标字段属性值
          const el = info.componentProps.options.find((el) => el.value === values[targetFiled]);
          if (el) {
            params[field] = el.label;
          }
        }
      } else if (formModelHiddenType === VALUE_FIELD_TYPE) {
        //  默认取初始值
        params[field] = defaultValue || componentProps.defaultValue;
      }
    }
  }

  return params;
};

function getDefValue(
  componentType: ComponentTypeEnum,
  {
    defValue,
    dateValue,
    defaultUserSelectValue,
    selectType,
    defaultMultipleValue,
    defaultDateValue,
  },
): string | any[] {
  if ([ComponentTypeEnum.RangePicker, ComponentTypeEnum.TimeRangePicker].includes(componentType)) {
    if (defaultDateValue && defaultDateValue.length) {
      return defaultDateValue;
    }
    return [];
  }

  if ([ComponentTypeEnum.DatePicker, ComponentTypeEnum.TimePicker].includes(componentType)) {
    return dateValue;
  }

  if (SELECT_COM_TYPE.includes(componentType) && selectType === MULTIPLE_SELECT)
    return defaultMultipleValue ?? [];

  if (SELECT_DEPT_USER_ROLE_POST_TYPE.includes(componentType)) {
    return defaultUserSelectValue;
  }
  return defValue;
}

function getComponent(
  componentType: Number,
  dataSourceType: string,
  readonly = false,
): ComponentType {
  // partReadonly
  if (readonly) return 'Readonly';
  if (componentType === ComponentTypeEnum.ApiSelect && dataSourceType !== DATA_SOURCE_TYPE_TYPE) {
    return 'ApiSelect';
  }

  return (ComponentTypeEnum[componentType as ComponentTypeEnum] as ComponentType) || DEFAULT_COM;
}

function setHideFieldValue({
  field,
  // dataSourceType,
  componentType,
  componentProps,
  hideFields,
  hideParams,
}: {
  field: string;
  // dataSourceType: string;
  componentType: ComponentTypeEnum;
  componentProps: Indexable;
  hideFields: Indexable[];
  hideParams: Indexable;
}) {
  if (SELECT_COM_TYPE.includes(componentType) && hideFields.length) {
    //  隐藏字段类型为目标录入字段 并且 存在目标字段
    const hideFieldInfo = hideFields.find(
      (el) => el.formModelHiddenType === TARGET_FIELD_TYPE && el.targetFiled === field,
    );

    // 如果有则创建change事件更新隐藏字段的值
    if (hideFieldInfo) {
      componentProps.onChange = (_, data) => {
        let label = '';
        if (isObject(data)) {
          label = data.label;
        } else {
          label = data;
        }
        hideParams[hideFieldInfo.field] = label;
      };
    }
  }
}

function getFormAutoLink({ componentType }) {
  if (
    [
      ComponentTypeEnum.SubTable,
      ComponentTypeEnum.HChooseUser,
      ComponentTypeEnum.HChooseDept,
      ComponentTypeEnum.HChooseRole,
      ComponentTypeEnum.HChoosePost,
      ComponentTypeEnum.HChooseComprehensiveBase,
      ComponentTypeEnum.HChooseUserAll,
      ComponentTypeEnum.HButtonZOffice,
    ].includes(componentType)
  )
    return false;
}

export function getFormSchemas(
  formDesignInfo: Indexable, // 模型配置数据
  formState: Partial<FormState>, // 表单传参和类型数据
  callbackMap: {
    formActionType?: FormActionType; //当前表单方法
    parentFormActionType?: Partial<FormActionType>; // 父级表单方法
    getRecord?: Fn;
  },
): Partial<{
  formConfig: FormProps;
  hideField: Indexable[];
  fields: Indexable[];
}> {
  if (!formDesignInfo) return {};
  const {
    partReadonly,
    readonly,
    hideParams = {},
    rangeTimeMap = new Map(),
    echoData = {},
  } = formState;
  //  配置表单属性及字段处理
  const {
    schemas: arr,
    layout = 'horizontal',
    labelSpan,
    labelAlign,
    colSpan,
    isReadonly,
    baseColProps,
    labelCol,
    colon,
    labelWidth,
    wrapperCol,
  } = formDesignInfo;
  if (!arr || !arr.length) return {};
  const fields = arr.filter((el) => el.componentType !== FIELD_TYPE_HIDE);

  const hideField = arr.filter((el) => el.componentType === FIELD_TYPE_HIDE);

  const schemas = arr.map((el) => {
    const {
      field,
      label,
      colSpan,
      helpMessage,
      helpBefore,
      helpAfter,
      labelSpan,
      labelWidth: subLabelWidth,
      updateType,
      componentType,
      dataSourceType,
      defaultDateValue,
      defaultMultipleValue,
      defaultUserSelectValue,
      dateValue,
      valueFormat,
      itemProps: flushItemProps,
      colProps: itemColProps,
      simpleChildTable,
      defaultValue: parseValue,
    } = el;

    //  判断子控件labelCol无配置 则走父级配置
    if (flushItemProps) {
      if (!flushItemProps.labelCol || flushItemProps.labelCol.span === 0) {
        flushItemProps.labelCol = {
          ...(labelCol || {}),
        };
        // 如果父级栅格为0则删除此字段 让 labelWidth 生效
        if (flushItemProps.labelCol.span === 0) {
          delete flushItemProps.labelCol.span;
        }
      }
      // 控件宽度
      if (!flushItemProps.wrapperCol || flushItemProps.wrapperCol.span === 0) {
        flushItemProps.wrapperCol = wrapperCol || {};
      }
    }
    //  判断子组件行宽无配置则走父级配置
    if (itemColProps) {
      if (itemColProps.span === 0) {
        delete itemColProps.span;
      }
    }
    const currentLabelWidth = getCSSValue(subLabelWidth || labelWidth);

    const { itemProps, colProps } = getItemColConfig(labelSpan, colSpan, {
      layout,
      flushItemProps,
      itemColProps,
      subLabelWidth,
    });

    // 设置默认值
    const defaultValue =
      echoData[field] ??
      getDefValue(componentType, {
        selectType: el.componentProps.selectType,
        defValue: parseValue || el.componentProps.defaultValue,
        defaultMultipleValue,
        defaultUserSelectValue,
        dateValue,
        defaultDateValue,
      });

    getTimeFormat({
      field,
      componentType,
      valueFormat,
      rangeTimeMap,
    });

    // 如果是只读 只处理布局和上传组件
    if (isReadonly && readonly) {
      const item: FormSchema = {
        field,
        label,
        labelWidth: currentLabelWidth,
        itemProps,
        colProps,
        defaultValue,
        ifShow: componentType !== FIELD_TYPE_HIDE,
        componentProps: {
          type: ComponentTypeEnum[componentType],
          simpleChildTable,
          disabled: true,
        },
        component:
          componentType === ComponentTypeEnum.HFormTitle
            ? (ComponentTypeEnum[componentType] as ComponentType)
            : 'Readonly',
      };
      return item;
    }
    // 是否禁用
    const dynamicDisabled = updateType === DISABLED_TYPE;

    const component = getComponent(componentType, dataSourceType, partReadonly && dynamicDisabled);

    const propsParams = {
      ...omit(el.componentProps, ['componentProps', 'component', 'componentType']),
      ...pick(el, [
        'event',
        'component',
        'componentType',
        'dataSourceType',
        'dataType',
        'dictType',
        'field',
        'formModelDataType',
        'formModelHiddenType',
        'formModelShowType',
        'formParamType',
        'modelFileServerType',
        'resourceCode',
        'resourceType',
        'updateType',
        'targetFiled',
        'simpleChildTable',
      ]),
      component,
      options: el.componentProps.options || [],
      defaultValue,
    };

    const middleComponentProps = setCustomProps(propsParams, formState, callbackMap);

    // 下拉选项，目录下拉类型并且是获取数据库并且是获取数据库数据时并且存在隐藏字段时
    setHideFieldValue({
      field,
      // dataSourceType,
      componentType,
      componentProps: middleComponentProps,
      hideFields: hideField,
      hideParams,
    });

    // 禁用后不显示必传
    const required = !dynamicDisabled && updateType === REQUIRED_TYPE;
    const autoLink = getFormAutoLink({
      componentType,
    });

    itemProps.autoLink = autoLink;

    const rules = getRules({
      ...el,
      required,
      modelId: formState.modelId,
      componentProps: middleComponentProps,
    })!;

    const item: FormSchema = {
      field,
      label,
      labelWidth: currentLabelWidth,
      required,
      defaultValue,
      dynamicDisabled,
      rules,
      helpMessage,
      helpBefore,
      helpAfter,
      ifShow: componentType !== FIELD_TYPE_HIDE,
      itemProps,
      colProps,
      componentProps: middleComponentProps,
      component,
    };
    const schema = setCustomEvent(item, formState, callbackMap);

    return schema;
  });

  const rowProps: FormProps['rowProps'] = {
    gutter: 0,
  };

  const lgColSpan = colSpan ?? 24;
  let span = labelSpan ?? 3;
  if (layout === 'vertical') {
    span = colSpan;
  }

  const formConfig: FormProps = {
    layout,
    labelAlign,
    schemas,
    colon,
    rowProps,
    labelWidth,
    labelCol: labelCol || { span },
    baseColProps: baseColProps || { lg: lgColSpan, md: 24, span: lgColSpan },
  };

  return { formConfig, fields, hideField };
}

function getFormCustomEvent(data: Indexable): Indexable<Fn> {
  const { event = {} } = data || {};

  const fnEvnt: Indexable<Fn> = {};
  if (event && !isEmpty(event)) {
    Object.keys(event).forEach((eventName) => {
      const item = event[eventName] as any;
      if (item.funcList && item.funcList.length) {
        const funcList = item.funcList.map(({ scriptText }) => {
          return new Function(
            'context',
            'args',
            'defHttp',
            `return (async function(context,args,defHttp){
              ${scriptText};
            })
          (context,args,defHttp)`,
          );
        });

        // 合并生成函数
        fnEvnt[eventName] = mergeFunctions(...funcList);
      }
    });
  }

  return fnEvnt;
}

function getTimeFormat({ field, componentType, valueFormat, rangeTimeMap }) {
  if (
    [
      ComponentTypeEnum.DatePicker,
      ComponentTypeEnum.TimePicker,
      ComponentTypeEnum.RangePicker,
      ComponentTypeEnum.TimeRangePicker,
    ].includes(componentType)
  ) {
    rangeTimeMap.set(field, valueFormat || 'YYYY-MM-DD HH:mm:ss');
  }
}
